package com.dong.web.database;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.map.CaseInsensitiveMap;

import com.dong.web.util.PrintUtil;

@SuppressWarnings({ "rawtypes", "unchecked" })
public final class JdbcHandler {

	private PreparedStatement prepareStatement = null;

	private Connection conn = null;

	private ResultSet rs = null;

	private boolean success = false;

	public JdbcHandler(Connection conn) {
		this.conn = conn;
	}

	public JdbcHandler() {
		this(null);
	}

	public void setConnection(Connection conn) {
		this.conn = conn;
	}

	public Connection getConnection() {
		return this.conn;
	}

	public void setAutoCommit(boolean auto) {
		try {
			conn.setAutoCommit(auto);
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	public Boolean getAutoCommit() {
		try {
			return conn.getAutoCommit();
		} catch (SQLException e) {
			e.printStackTrace();
			return null;
		}
	}

	public boolean getSuccess() {
		return this.success;
	}

	private void setSuccess() {
		if (!success) {
			success = true;
		}
	}

	private void setParams(String sql, Object[] objects) throws SQLException {
		prepareStatement = conn.prepareStatement(sql);
		if (objects != null) {
			int length = objects.length;
			for (int i = 0; i < length; i++) {
				prepareStatement.setObject(i + 1, objects[i]);
			}
		}
	}

	public Integer getMaxRow(String sql, Object[] objects) {
		try {
			setParams(sql, objects);
			rs = prepareStatement.executeQuery();
			rs.last();
			return rs.getRow();
		} catch (Exception e) {
			e.printStackTrace();
			setSuccess();
			return null;
		} finally {
			this.close();
		}
	}

	public Map queryForMap(String sql) {
		return this.queryForMap(sql, null);
	}

	public Map queryForMap(String sql, Object[] objects) {
		Map rn = null;
		try {
			setParams(sql, objects);
			rs = prepareStatement.executeQuery();
			rs.last();
			int row = rs.getRow();
			if (row > 1) {
				throw new SQLException("Number of lines is greater than one!");
			} else if (row <= 0) {
				return null;
			} else {
				rs.beforeFirst();
				String[] columns = JdbcUtils.getColumns(rs);
				while (rs.next()) {
					rn = new CaseInsensitiveMap();
					for (String column : columns) {
						Object obj = rs.getObject(column);
						rn.put(column, obj);
					}
					break;
				}
				return rn;
			}
		} catch (SQLException e) {
			e.printStackTrace();
			setSuccess();
			return null;
		} finally {
			this.close();
		}
	}

	public List queryForList(String sql) {
		return this.queryForList(sql, null);
	}

	public List queryForList(String sql, Object[] objects) {
		List rn = new ArrayList();
		try {
			setParams(sql, objects);
			rs = prepareStatement.executeQuery();
			String[] columns = JdbcUtils.getColumns(rs);
			while (rs.next()) {
				Map m = new CaseInsensitiveMap();
				for (String column : columns) {
					Object obj = rs.getObject(column);
					m.put(column, obj);
				}
				rn.add(m);
			}
			return rn;
		} catch (SQLException e) {
			e.printStackTrace();
			setSuccess();
			return null;
		} finally {
			this.close();
		}
	}

	public Integer queryForInt(String sql) {
		return this.queryForInt(sql, null);
	}

	public Integer queryForInt(String sql, Object[] objects) {
		try {
			setParams(sql, objects);
			rs = prepareStatement.executeQuery();
			int rn = 0;
			while (rs.next()) {
				rn = rs.getInt(1);
				break;
			}
			return rn;
		} catch (SQLException e) {
			e.printStackTrace();
			setSuccess();
			return null;
		} finally {
			this.close();
		}
	}

	public Object queryForObject(String sql) {
		return this.queryForObject(sql, null);
	}

	public Object queryForObject(String sql, Object[] objects) {
		try {
			setParams(sql, objects);
			rs = prepareStatement.executeQuery();
			Object rn = 0;
			while (rs.next()) {
				rn = rs.getObject(1);
				break;
			}
			return rn;
		} catch (SQLException e) {
			e.printStackTrace();
			setSuccess();
			return null;
		} finally {
			this.close();
		}
	}

	public boolean update(String sql) {
		return this.update(sql, null);
	}

	public Boolean update(String sql, Object[] objects) {
		try {
			setParams(sql, objects);
			Boolean obj = prepareStatement.execute();
			return obj;
		} catch (SQLException e) {
			e.printStackTrace();
			setSuccess();
			return null;
		} finally {
			this.close();
		}
	}

	public void execute(JdbcCallback jdbcCallback) {
		try {
			boolean rn = jdbcCallback.call(conn);
			if (!rn) {
				setSuccess();
			}
		} catch (Exception e) {
			e.printStackTrace();
			setSuccess();
		}
	}

	public void closeConnection() {
		try {
			new PrintUtil().println(this.getClass(), "closeConnection", conn);
			if (conn != null) {
				conn.close();
				conn = null;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void close() {
		try {
			new PrintUtil().println(this.getClass(), "close", new Object[] {
					rs, prepareStatement });
			if (rs != null) {
				rs.close();
				rs = null;
			}
			if (prepareStatement != null) {
				prepareStatement.close();
				prepareStatement = null;
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

}
